package com.example.demo.common;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.util.Properties;

/**
 * type 拦截的类型 四大对象之一(Executor,ResultSetHandler,ParameterHandler,StatementHandler)
 * method 拦截的方法
 * args 参数,高版本需要加个Integer.class参数,不然会报错
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PaginationInterceptor implements Interceptor {

    //每页显示的条目数
    private int pageSize;
    //当前现实的页数
    private int currPage;
    //数据库类型
    private String dbType;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取StatementHandler，默认是RoutingStatementHandler
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        //获取statementHandler包装类
        MetaObject MetaObjectHandler = SystemMetaObject.forObject(statementHandler);

        //分离代理对象链
        while (MetaObjectHandler.hasGetter("h")) {
            Object obj = MetaObjectHandler.getValue("h");
            MetaObjectHandler = SystemMetaObject.forObject(obj);
        }

        while (MetaObjectHandler.hasGetter("target")) {
            Object obj = MetaObjectHandler.getValue("target");
            MetaObjectHandler = SystemMetaObject.forObject(obj);
        }

        //获取连接对象
        //Connection connection = (Connection) invocation.getArgs()[0];


        //object.getValue("delegate");  获取StatementHandler的实现类

        //获取查询接口映射的相关信息
        MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");
        String mapId = mappedStatement.getId();

        //statementHandler.getBoundSql().getParameterObject();


        //获取进行数据库操作时管理参数的handler
        ParameterHandler parameterHandler = (ParameterHandler) MetaObjectHandler.getValue("delegate.parameterHandler");
        //获取请求时的参数
        Object parameterObject = parameterHandler.getParameterObject();
        if (parameterObject instanceof BaseEntity) {
            BaseEntity baseEntity = (BaseEntity) parameterHandler.getParameterObject();
            //也可以这样获取
            //paraObject = (Map<String, Object>) statementHandler.getBoundSql().getParameterObject();

            Pagination pagination = baseEntity.getPagination();
            if (pagination != null) {
                currPage = pagination.getPage();
                pageSize = pagination.getLimit();

                String sql = (String) MetaObjectHandler.getValue("delegate.boundSql.sql");
                //也可以通过statementHandler直接获取
                //sql = statementHandler.getBoundSql().getSql();

                pagination.setCount(PaginationHelpler.getCount(sql, null, mappedStatement, parameterObject,
                        mappedStatement.getBoundSql(parameterObject)));

                //构建分页功能的sql语句
                String limitSql;
                sql = sql.trim();
                int offset = (currPage - 1) * pageSize;
                limitSql = PaginationHelpler.getLimitString(sql, offset, Integer.toString(offset), Integer.toString(pageSize));

                //将构建完成的分页sql语句赋值个体'delegate.boundSql.sql'
                MetaObjectHandler.setValue("delegate.boundSql.sql", limitSql);
            }
        }

        //调用原对象的方法，进入责任链的下一级
        return invocation.proceed();
    }

    //获取代理对象
    @Override
    public Object plugin(Object o) {
        //生成object对象的动态代理对象
        return Plugin.wrap(o, this);
    }

    //设置代理对象的参数
    @Override
    public void setProperties(Properties properties) {
        //如果项目中分页的pageSize是统一的，也可以在这里统一配置和获取，这样就不用每次请求都传递pageSize参数了。参数是在配置拦截器时配置的。
        String limit = properties.getProperty("limit", "10");
        this.pageSize = Integer.valueOf(limit);
        this.dbType = properties.getProperty("dbType", "oracle");
    }

}
